<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
    <meta name="description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta property="og:title" content="Dilation | 3D Game Shaders For Beginners" />
    <meta property="og:description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta property="og:image" content="https://i.imgur.com/brdytrF.png" />
    <meta name="twitter:title" content="Dilation | 3D Game Shaders For Beginners" />
    <meta name="twitter:description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta name="twitter:image" content="https://i.imgur.com/brdytrF.png" />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="author" content="David Lettier" />
    <title>Dilation | 3D Game Shaders For Beginners</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
    </style>
    <style>
      code.sourceCode > span { display: inline-block; line-height: 1.25; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      code.sourceCode > span:empty { height: 1.2em; }
      .sourceCode { overflow: visible; }
      code.sourceCode { white-space: pre; position: relative; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      code.sourceCode { white-space: pre-wrap; }
      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          background-color: #232629;
          color: #7a7c7d;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }
      div.sourceCode
        { color: #cfcfc2; background-color: #232629; }
      @media screen {
      code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span. { color: #cfcfc2; } /* Normal */
      code span.al { color: #95da4c; } /* Alert */
      code span.an { color: #3f8058; } /* Annotation */
      code span.at { color: #2980b9; } /* Attribute */
      code span.bn { color: #f67400; } /* BaseN */
      code span.bu { color: #7f8c8d; } /* BuiltIn */
      code span.cf { color: #fdbc4b; } /* ControlFlow */
      code span.ch { color: #3daee9; } /* Char */
      code span.cn { color: #27aeae; } /* Constant */
      code span.co { color: #7a7c7d; } /* Comment */
      code span.cv { color: #7f8c8d; } /* CommentVar */
      code span.do { color: #a43340; } /* Documentation */
      code span.dt { color: #2980b9; } /* DataType */
      code span.dv { color: #f67400; } /* DecVal */
      code span.er { color: #da4453; } /* Error */
      code span.ex { color: #0099ff; } /* Extension */
      code span.fl { color: #f67400; } /* Float */
      code span.fu { color: #8e44ad; } /* Function */
      code span.im { color: #27ae60; } /* Import */
      code span.in { color: #c45b00; } /* Information */
      code span.kw { color: #cfcfc2; } /* Keyword */
      code span.op { color: #cfcfc2; } /* Operator */
      code span.ot { color: #27ae60; } /* Other */
      code span.pp { color: #27ae60; } /* Preprocessor */
      code span.re { color: #2980b9; } /* RegionMarker */
      code span.sc { color: #3daee9; } /* SpecialChar */
      code span.ss { color: #da4453; } /* SpecialString */
      code span.st { color: #f44f4f; } /* String */
      code span.va { color: #27aeae; } /* Variable */
      code span.vs { color: #da4453; } /* VerbatimString */
      code span.wa { color: #da4453; } /* Warning */
    </style>
    <!--[if lt IE 9]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
<p><a href="sharpen.html"><span class="emoji" data-emoji="arrow_backward">◀️</span></a> <a href="index.html"><span class="emoji" data-emoji="arrow_double_up">⏫</span></a> <a href="#"><span class="emoji" data-emoji="arrow_up_small">🔼</span></a> <a href="#copyright"><span class="emoji" data-emoji="arrow_down_small">🔽</span></a> <a href="film-grain.html"><span class="emoji" data-emoji="arrow_forward">▶️</span></a></p>
<h1 id="3d-game-shaders-for-beginners">3D Game Shaders For Beginners</h1>
<h2 id="dilation">Dilation</h2>
<p><img src="https://i.imgur.com/z751O74.gif" alt="Dilation" /></p>
<p>Dilation dilates or enlarges the brighter areas of an image while at the same time, contracts or shrinks the darker areas of an image. This tends to create a pillowy look. You can use dilation for a glow/bloom effect or to add bokeh to your <a href="depth-of-field.html">depth of field</a>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a>  <span class="co">// ...</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a>  <span class="dt">int</span>   size         = <span class="dt">int</span>(parameters.x);</span>
<span id="cb1-4"><a href="#cb1-4"></a>  <span class="dt">float</span> separation   =     parameters.y;</span>
<span id="cb1-5"><a href="#cb1-5"></a>  <span class="dt">float</span> minThreshold = <span class="fl">0.1</span>;</span>
<span id="cb1-6"><a href="#cb1-6"></a>  <span class="dt">float</span> maxThreshold = <span class="fl">0.3</span>;</span>
<span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>The <code>size</code> and <code>separation</code> parameters control how dilated the image becomes. A larger <code>size</code> will increase the dilation at the cost of performance. A larger <code>separation</code> will increase the dilation at the cost of quality. The <code>minThreshold</code> and <code>maxThreshold</code> parameters control which parts of the image become dilated.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a>  <span class="co">// ...</span></span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a>  vec2 texSize   = textureSize(colorTexture, <span class="dv">0</span>).xy;</span>
<span id="cb2-4"><a href="#cb2-4"></a>  vec2 fragCoord = gl_FragCoord.xy;</span>
<span id="cb2-5"><a href="#cb2-5"></a></span>
<span id="cb2-6"><a href="#cb2-6"></a>  fragColor = texture(colorTexture, fragCoord / texSize);</span>
<span id="cb2-7"><a href="#cb2-7"></a></span>
<span id="cb2-8"><a href="#cb2-8"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>Sample the color at the current fragment's position.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a>  <span class="co">// ...</span></span>
<span id="cb3-2"><a href="#cb3-2"></a></span>
<span id="cb3-3"><a href="#cb3-3"></a>  <span class="dt">float</span>  mx = <span class="fl">0.0</span>;</span>
<span id="cb3-4"><a href="#cb3-4"></a>  vec4  cmx = fragColor;</span>
<span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a>  <span class="cf">for</span> (<span class="dt">int</span> i = -size; i &lt;= size; ++i) {</span>
<span id="cb3-7"><a href="#cb3-7"></a>    <span class="cf">for</span> (<span class="dt">int</span> j = -size; j &lt;= size; ++j) {</span>
<span id="cb3-8"><a href="#cb3-8"></a>      <span class="co">// ...</span></span>
<span id="cb3-9"><a href="#cb3-9"></a>    }</span>
<span id="cb3-10"><a href="#cb3-10"></a>  }</span>
<span id="cb3-11"><a href="#cb3-11"></a></span>
<span id="cb3-12"><a href="#cb3-12"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>Loop through a <code>size</code> by <code>size</code> window, centered at the current fragment position. As you loop, find the brightest color based on the surrounding greyscale values.</p>
<p><img src="https://i.imgur.com/X3uIyIL.png" alt="Dilation Window" /></p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a>      <span class="co">// ...</span></span>
<span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb4-3"><a href="#cb4-3"></a>      <span class="co">// For a rectangular shape.</span></span>
<span id="cb4-4"><a href="#cb4-4"></a>      <span class="co">//if (false);</span></span>
<span id="cb4-5"><a href="#cb4-5"></a></span>
<span id="cb4-6"><a href="#cb4-6"></a>      <span class="co">// For a diamond shape;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a>      <span class="co">//if (!(abs(i) &lt;= size - abs(j))) { continue; }</span></span>
<span id="cb4-8"><a href="#cb4-8"></a></span>
<span id="cb4-9"><a href="#cb4-9"></a>      <span class="co">// For a circular shape.</span></span>
<span id="cb4-10"><a href="#cb4-10"></a>      <span class="cf">if</span> (!(distance(vec2(i, j), vec2(<span class="dv">0</span>, <span class="dv">0</span>)) &lt;= size)) { <span class="cf">continue</span>; }</span>
<span id="cb4-11"><a href="#cb4-11"></a></span>
<span id="cb4-12"><a href="#cb4-12"></a>      <span class="co">// ...</span></span></code></pre></div>
<p>The window shape will determine the shape of the dilated parts of the image. For a rectangular shape, you can use every fragment covered by the window. For any other shape, skip the fragments that fall outside the desired shape.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a>      <span class="co">// ...</span></span>
<span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a>      vec4 c =</span>
<span id="cb5-4"><a href="#cb5-4"></a>        texture</span>
<span id="cb5-5"><a href="#cb5-5"></a>          ( colorTexture</span>
<span id="cb5-6"><a href="#cb5-6"></a>          ,   ( gl_FragCoord.xy</span>
<span id="cb5-7"><a href="#cb5-7"></a>              + (vec2(i, j) * separation)</span>
<span id="cb5-8"><a href="#cb5-8"></a>              )</span>
<span id="cb5-9"><a href="#cb5-9"></a>            / texSize</span>
<span id="cb5-10"><a href="#cb5-10"></a>          );</span>
<span id="cb5-11"><a href="#cb5-11"></a></span>
<span id="cb5-12"><a href="#cb5-12"></a>      <span class="co">// ...</span></span></code></pre></div>
<p>Sample a fragment color from the surrounding window.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1"></a>      <span class="co">// ...</span></span>
<span id="cb6-2"><a href="#cb6-2"></a></span>
<span id="cb6-3"><a href="#cb6-3"></a>      <span class="dt">float</span> mxt = dot(c.rgb, vec3(<span class="fl">0.21</span>, <span class="fl">0.72</span>, <span class="fl">0.07</span>));</span>
<span id="cb6-4"><a href="#cb6-4"></a></span>
<span id="cb6-5"><a href="#cb6-5"></a>      <span class="co">// ...</span></span></code></pre></div>
<p>Convert the sampled color to a greyscale value.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a>      <span class="co">// ...</span></span>
<span id="cb7-2"><a href="#cb7-2"></a></span>
<span id="cb7-3"><a href="#cb7-3"></a>      <span class="cf">if</span> (mxt &gt; mx) {</span>
<span id="cb7-4"><a href="#cb7-4"></a>        mx  = mxt;</span>
<span id="cb7-5"><a href="#cb7-5"></a>        cmx = c;</span>
<span id="cb7-6"><a href="#cb7-6"></a>      }</span>
<span id="cb7-7"><a href="#cb7-7"></a></span>
<span id="cb7-8"><a href="#cb7-8"></a>      <span class="co">// ...</span></span></code></pre></div>
<p>If the sampled greyscale value is larger than the current maximum greyscale value, update the maximum greyscale value and its corresponding color.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a>  <span class="co">// ...</span></span>
<span id="cb8-2"><a href="#cb8-2"></a></span>
<span id="cb8-3"><a href="#cb8-3"></a>  fragColor.rgb =</span>
<span id="cb8-4"><a href="#cb8-4"></a>    mix</span>
<span id="cb8-5"><a href="#cb8-5"></a>      ( fragColor.rgb</span>
<span id="cb8-6"><a href="#cb8-6"></a>      , cmx.rgb</span>
<span id="cb8-7"><a href="#cb8-7"></a>      , smoothstep(minThreshold, maxThreshold, mx)</span>
<span id="cb8-8"><a href="#cb8-8"></a>      );</span>
<span id="cb8-9"><a href="#cb8-9"></a></span>
<span id="cb8-10"><a href="#cb8-10"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>The new fragment color is a mixture between the existing fragment color and the brightest color found. If the maximum greyscale value found is less than <code>minThreshold</code>, the fragment color is unchanged. If the maximum greyscale value is greater than <code>maxThreshold</code>, the fragment color is replaced with the brightest color found. For any other case, the fragment color is a mix between the current fragment color and the brightest color.</p>
<h3 id="source">Source</h3>
<ul>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx" target="_blank" rel="noopener noreferrer">main.cxx</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert" target="_blank" rel="noopener noreferrer">basic.vert</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/dilation.frag" target="_blank" rel="noopener noreferrer">dilation.frag</a></li>
</ul>
<h2 id="copyright">Copyright</h2>
<p>(C) 2020 David Lettier <br> <a href="https://www.lettier.com">lettier.com</a></p>
<p><a href="sharpen.html"><span class="emoji" data-emoji="arrow_backward">◀️</span></a> <a href="index.html"><span class="emoji" data-emoji="arrow_double_up">⏫</span></a> <a href="#"><span class="emoji" data-emoji="arrow_up_small">🔼</span></a> <a href="#copyright"><span class="emoji" data-emoji="arrow_down_small">🔽</span></a> <a href="film-grain.html"><span class="emoji" data-emoji="arrow_forward">▶️</span></a></p>
  </body>
</html>
